package battleofthejavasea;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Murata, Saito, Santana
 * Classe utilizada para se conectar a um módulo servidor instanciado.
 * É o módulo cliente embutido no jogo.
 * O módulo cliente é disparado em uma nova Thread assim que o usuário opta pela
 * conexão a um servidor já existente.
 */
public class BTJSClientInnerModule implements Runnable {

    private ObjectInputStream in = null;
    private ObjectOutputStream out = null;
    private String hostname;
    private int port;
    private Socket client;
    //Objeto necessário para se ter acesso às variáveis de jogo.
    private BTJSGameFrame btjsGF;
    //ArrayList para manter track de posições já c/ objetos
    private ArrayList<Integer> alRenderTrack = new ArrayList<Integer>();
    //Objeto para verificar trocas de turno
    private boolean gameTurnChangeTracker;
    //Index para checar a quantidade de vezes que a variável foi alterada.
    private int checkChanges = 0;


    //Inicia cliente informando hostname e porta que o servidor aguarda nova conexão.
    public BTJSClientInnerModule(String hostname, int port, BTJSGameFrame btjsGF){
        this.hostname = hostname;
        this.port = port;
        this.btjsGF = btjsGF;
        this.gameTurnChangeTracker = btjsGF.btjsGameLogic.getPlayer2BoatsCoords().getIsMyTurn();
        btjsGF.btjsGameLogic.getPlayer1BoatsCoords().setIsMyTurn(false);
    }

    //Necessário em decorrência da implementação por Runnable
    @Override
    public void run() {
        try{
                //Conecta-se ao servidor
                serverConnect(this.hostname, this.port);
                //Obtém fluxos de E/S
                getStreams();
                //Entra em loop para troca de informações e variáveis do jogo.
                sendReceiveBoatsPositioningInfo();
             } catch (InterruptedException ex) {
            Logger.getLogger(BTJSClientInnerModule.class.getName()).log(Level.SEVERE, null, ex);
        } catch (UnknownHostException ex) {
                Logger.getLogger(BTJSClientInnerModule.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IOException ex) {
                Logger.getLogger(BTJSClientInnerModule.class.getName()).log(Level.SEVERE, null, ex);
            } catch (ClassNotFoundException ex) {
                Logger.getLogger(BTJSClientInnerModule.class.getName()).log(Level.SEVERE, null, ex);
        } finally{
           closeConnection();
        }

    }

    //Conectar a servidor
    private void serverConnect(String hostname,int port) throws UnknownHostException, IOException{
        client = new Socket(hostname, port);
    }

    //Obtém fluxos de entrada e saída
    private void getStreams() throws IOException{
        out = new ObjectOutputStream(client.getOutputStream());
        out.flush();

        in = new ObjectInputStream(client.getInputStream());
    }

    //Envia/Recebe informações de posicionamento de barcos
    public void sendReceiveBoatsPositioningInfo() throws IOException, ClassNotFoundException, InterruptedException
    {
        while(true)
        {
            //Efetua sleep de 0,5 segundo para ecnomizar recursos.
            Thread.currentThread().sleep(500);

            //Obtém do servidor o posicionamento dos barcos
            BTJS_DTOPlayerBoats obj = (BTJS_DTOPlayerBoats)in.readObject();
            btjsGF.btjsGameLogic.setPlayer2Boats(obj);

            //Envia informações do posicionamento dos barcos do jogador cliente.
            sendData(btjsGF.btjsGameLogic.getPlayer1BoatsCoords());

            //Marca fogo inimigo na minha tela.
            getEnemyShots();

            //Trackeia turnos dos jogadores
            trackTurnChange();
        }
     }


    //Fecha a conexão
    private void closeConnection(){
        try{
            out.close();
            in.close();
            client.close();
        }
        catch(IOException exceptionIO){
            exceptionIO.printStackTrace();
        }
    }

    //Envia dados como Object.
    public void sendData(Object obj){
        try{
            out.writeObject(obj);
            out.flush();
            //Reseta objeto output, para não enviar flag de objeto "já enviado".
            out.reset();
        }
        catch(IOException exceptionIO){
            exceptionIO.printStackTrace();
        }
    }

    //Marca símbolos de fogo inimigo no tabuleiro local.
    private void getEnemyShots() throws FileNotFoundException{
        //Objeto para marcar tiros
        BTJSGameSymbols btjsGS = new BTJSGameSymbols();
        //Interpretador de posicionamento para a cena 3d
        BTJSPositionInterpreter btjsPositionInterpreter = new BTJSPositionInterpreter();

        if(btjsGF.btjsGameLogic.getPlayer2BoatsCoords().currentIndexEnemyShots != 0)
            for(int i = 0; i < 100; i ++)  {
                    //Se bup não for nulo
                    BTJS_DTOBoatUnitPosition bup = btjsGF.btjsGameLogic.getPlayer2BoatsCoords().getEnemyShotPositions()[i];
                        if(bup != null)
                        //Se foi atingida e ainda não possui símbolo renderizado, adiciona esfera vermelha em meu tabuleiro
                        if(bup.hasBeenShot && !alRenderTrack.contains(i)){
                            btjsGF.simpleUniverse.addBranchGraph((btjsGS.hitMe(btjsPositionInterpreter.getColumnValue(bup.column), btjsPositionInterpreter.getRowValue(bup.row))));
                            alRenderTrack.add(i);
                        }
                        else if(!bup.hasBeenShot && !alRenderTrack.contains(i)){
                            btjsGF.simpleUniverse.addBranchGraph((btjsGS.missedMe(btjsPositionInterpreter.getColumnValue(bup.column), btjsPositionInterpreter.getRowValue(bup.row))));
                            alRenderTrack.add(i);
                        }
            }
    }

    //Monitora troca de turnos
    private void trackTurnChange(){

        if(gameTurnChangeTracker != btjsGF.btjsGameLogic.getPlayer2BoatsCoords().getIsMyTurn()){
            checkChanges++;
            gameTurnChangeTracker = btjsGF.btjsGameLogic.getPlayer2BoatsCoords().getIsMyTurn();
            //Ajuste em decorrência das alterações realizadas pelas Threads C/S
            if(checkChanges == 2){
                gameTurnChangeTracker = btjsGF.btjsGameLogic.getPlayer2BoatsCoords().getIsMyTurn();
                btjsGF.btjsGameLogic.getPlayer1BoatsCoords().setIsMyTurn(!btjsGF.btjsGameLogic.getPlayer1BoatsCoords().getIsMyTurn()) ;
                checkChanges = 0;
            }
        }

    }
}
